﻿using System.Text;
using CppAst;
using Microsoft.VisualBasic;

namespace ast;

public static class TsTypeUtil
{
    public static string GetTsRefTypeString(CppReferenceType cppPointerType)
    {
        return GetTsPointerTypeString(new CppPointerType(cppPointerType.ElementType));
    }

    public static string GetTsPointerTypeString(CppPointerType cppPointerType)
    {
        if (ClassUtil.IsVoidAnyPtr(cppPointerType))
        {
            return "bigint";
        }

        var elementType = cppPointerType.ElementType;
        if (elementType is CppArrayType cppArrayType)
        {
            if (cppArrayType.ElementType is CppPrimitiveType || cppArrayType.ElementType is CppEnum)
            {
                // int(*)[4]
                if (cppArrayType.Size != -1)
                {
                    if (cppPointerType.ElementType is CppPrimitiveType)
                    {
                        return "BasicArrayWrapper" + ClassUtil.GetCppSimpleName(elementType);
                    }
                    else if (cppArrayType.ElementType is CppEnum arrayElmentCppEnum)
                    {
                        return "BasicWrapper" + ClassUtil.GetCppSimpleName(arrayElmentCppEnum.IntegerType);
                    }
                }
                else
                {
                    return GetTsTypeString(new CppPointerType(cppArrayType.ElementType));
                }
            }
            else
            {
                return "bigint";
            }
        }

        if (elementType is CppClass cppClass)
        {
            return GetTsTypeString(elementType);
        }

        if (elementType is CppPrimitiveType cppPrimitiveType)
        {
            return "BasicWrapper" + ClassUtil.GetCppSimpleName(elementType);
        }

        if (elementType is CppEnum cppEnum)
        {
            return "BasicWrapper" + ClassUtil.GetCppSimpleName(cppEnum.IntegerType);
        }

        return GetTsTypeString(elementType);
    }


    public static string GetTsTypeString(CppType cppType, bool parentIsArray = false)
    {
        if (ClassUtil.IsVoidAnyPtr(cppType))
        {
            return "bigint";
        }

        switch (cppType)
        {
            case CppPointerType cppPointerType:
                return GetTsPointerTypeString(cppPointerType);
            case CppReferenceType cppReferenceType:
                return GetTsRefTypeString(cppReferenceType);
            case CppPrimitiveType cppPrimitiveType:
                switch (cppPrimitiveType.Kind)
                {
                    case CppPrimitiveKind.Void:
                        return "";
                    case CppPrimitiveKind.Bool:
                        return "boolean";
                    case CppPrimitiveKind.WChar:
                        return "number";
                    case CppPrimitiveKind.SignedChar:
                        return "number";
                    case CppPrimitiveKind.Char:
                        return "byte";
                    case CppPrimitiveKind.Short:
                        return "number";
                    case CppPrimitiveKind.Int:
                        return "number";
                    case CppPrimitiveKind.Long:
                        return "bigint";
                    case CppPrimitiveKind.LongLong:
                        return "bigint";
                    case CppPrimitiveKind.UnsignedChar:
                        return "number";
                    case CppPrimitiveKind.UnsignedShort:
                        return "number";
                    case CppPrimitiveKind.UnsignedInt:
                        return "number";

                    case CppPrimitiveKind.UnsignedLongLong:
                        return "bigint";
                    case CppPrimitiveKind.Float:
                        return "number";
                    case CppPrimitiveKind.Double:
                        return "number";
                    case CppPrimitiveKind.LongDouble:
                        return "number";
                    default:
                        throw new Exception("primitive type not registered");
                }
            case CppEnum cppEnumType:
                //todo 添加枚举到Ts文件中
                return GetTsEnumName(cppEnumType);
            case CppClass cppClassType:
                return GetTsClassName(cppClassType);
            case CppArrayType cppArrayType:
                return "bigint";
            case CppUnexposedType unexposedType:
                break;
        }

        throw new Exception("Ts type not registered");
    }

    public static string GetTsClassName(CppClass cppClass)
    {
        var TsClassName = ClassUtil.FirstToUpper(cppClass.FullName);
        if (cppClass.TemplateKind == CppTemplateKind.TemplateSpecializedClass)
        {
            StringBuilder sb = new StringBuilder();
            string fullparent = cppClass.FullParentName;
            var useName = cppClass.Name;

            if (string.IsNullOrEmpty(fullparent))
            {
                sb.Append(useName);
            }
            else
            {
                sb.Append($"{fullparent}::{useName}");
            }


            for (int i = 0; i < cppClass.TemplateSpecializedArguments.Count; i++)
            {
                var tp = cppClass.TemplateSpecializedArguments[i];
                if (tp.ArgAsType != null)
                {
                    sb.Append(ClassUtil.FirstToUpper(tp.ArgAsType.FullName));
                }
                else if (tp.ArgString != null)
                {
                    sb.Append(ClassUtil.FirstToUpper(tp.ArgString));
                }
                else if (tp.ArgAsInteger != null)
                {
                    sb.Append(tp.ArgAsInteger);
                }
                else
                {
                    sb.Append(tp.ArgAsUnknown);
                }
            }

            TsClassName = sb.ToString();
            TsClassName = ClassUtil.FirstToUpper(TsClassName);
        }

        var implName = TsClassName + "";
        implName = implName.Replace("::", "");
        implName = implName.Replace(" ", "");
        implName = implName.Replace("<", "");
        implName = implName.Replace(">", "");
        implName = implName.Replace("*", "Ptr");
        implName = implName.Replace("&", "Ref");
        implName = implName.Replace("-", "");
        return implName;
    }

    public static string GetTsEnumName(CppEnum cppEnum)
    {
        var TsClassName = ClassUtil.FirstToUpper(cppEnum.FullName);
        var implName = "Enum" + TsClassName;
        implName = implName.Replace("::", "");
        // implName = implName.Replace("_", "");
        return implName;
    }

    public static string GetTsFunctionSign(ModuleInfo moduleInfo, CppFunction cppFunction)
    {
        //首先判断是否有过重载，如果重载了，按照被重复函数本身的签名来用
        //没有重载，判断有没有override函数,有override按照拼接签名,没有override则可以正常使用
        if (cppFunction.IsOverride)
        {
            var overrideCursor = cppFunction.OverrideCursors[0];
            var ok = moduleInfo.cursorToFunction.TryGetValue(overrideCursor, out var overrideFunction);
            if (!ok)
            {
                throw new Exception("Override cursor not found");
            }

            return GetTsFunctionSign(moduleInfo, overrideFunction);
        }

        var retName = cppFunction.Name;

        if (cppFunction.Parent is CppClass cppClass)
        {
            var classInfo = moduleInfo.FindClassInfoOfCppClass(cppClass);
            if (classInfo != null)
            {
                classInfo.FunctionInfos.TryGetValue(Strings.UCase(cppFunction.Name), out var functionInfo);
                if (functionInfo != null && functionInfo.Count > 0)
                {
                    for (int i = 0; i < cppFunction.Parameters.Count; i++)
                    {
                        var exportPara = cppFunction.Parameters[i];
                        var paraName = ClassUtil.GetCppSimpleName(ClassUtil.SimpleType(exportPara.Type));
                        retName += paraName;
                    }
                }
            }
        }

        return retName;
    }

    public static string GetTsFunctionName(ModuleInfo moduleInfo, FunctionInfo functionInfo)
    {
        var cppFunction = functionInfo.Function;
        var tsFunctionName = GetTsFunctionSign(moduleInfo, functionInfo.Function);
        if (cppFunction.IsConstructor)
        {
            tsFunctionName = "New" + tsFunctionName;
        }

        tsFunctionName = tsFunctionName.Replace("::", "_");
        tsFunctionName = tsFunctionName.Replace("<", "_");
        tsFunctionName = tsFunctionName.Replace(">", "");
        tsFunctionName = tsFunctionName.Replace(",", "_");
        tsFunctionName = tsFunctionName.Replace(" ", "");
        tsFunctionName = tsFunctionName.Replace("*", "Ptr");
        tsFunctionName = tsFunctionName.Replace("&", "Ref");
        tsFunctionName = tsFunctionName.Replace("-", "");
        return tsFunctionName;
    }

    public static bool SupportDirectProp(CppType cleanType)
    {
        switch (cleanType)
        {
            case CppPrimitiveType:
            case CppEnum:
                return true;
            case CppPointerType:
                return true;
            case CppClass:
                return true;
            default:
                return false;
        }
    }
}